#include <QtGui>
#include <QtOpenGL>

#include <math.h>
//#include <GL/glew.h>
#include "glwidget.h"
#include "qtlogo.h"


#include <GL/glext.h>
#ifndef GL_MULTISAMPLE
#define GL_MULTISAMPLE  0x809D
#endif

//! [0]
GLWidget::GLWidget(QWidget *parent)
    : QGLWidget(QGLFormat(QGL::SampleBuffers), parent)
{
    logo = 0;
    xRot = 0;
    yRot = 0;
    zRot = 0;

    farRatio=1.0f;

    qtGreen = QColor::fromCmykF(0.40, 0.0, 1.0, 0.0);
    qtPurple = QColor::fromCmykF(0.39, 0.39, 0.0, 0.0);

}
//! [0]

//! [1]
GLWidget::~GLWidget()
{
}
//! [1]

//! [2]
QSize GLWidget::minimumSizeHint() const
{
    return QSize(50, 50);
}
//! [2]

//! [3]
QSize GLWidget::sizeHint() const
//! [3] //! [4]
{
    return QSize(400, 400);
}
//! [4]

static void qNormalizeAngle(int &angle)
{
    while (angle < 0)
        angle += 360 * 16;
    while (angle > 360 * 16)
        angle -= 360 * 16;
}

//! [5]
void GLWidget::setXRotation(int angle)
{
    qNormalizeAngle(angle);
    if (angle != xRot) {
        xRot = angle;
        emit xRotationChanged(angle);
        updateGL();
    }
}
//! [5]

void GLWidget::setYRotation(int angle)
{
    qNormalizeAngle(angle);
    if (angle != yRot) {
        yRot = angle;
        emit yRotationChanged(angle);
        updateGL();
    }
}

void GLWidget::setZRotation(int angle)
{
    qNormalizeAngle(angle);
    if (angle != zRot) {
        zRot = angle;
        emit zRotationChanged(angle);
        updateGL();
    }
}

static char *source=
        "void main()"
        "{"
        "    vec3 normal, lightDir, viewVector, halfVector;"
        "    vec3 diffuse, ambient, globalAmbient, specular = vec3(0.0);"
        "    float NdotL,NdotHV;"

        "    vec2 pos = gl_TexCoord[0].xy - vec2(0.5, 0.5);"
        "    if(length(pos) > 0.5)"
        "        discard;"
        "    float r = 0.5;"
        "    float x = pos.x;"
        "    float y = pos.y;"
        "    float z = sqrt(r*r - x*x - y*y);"

        "    normal = normalize(vec3(x, y, z));"
        "    lightDir = normalize(vec3(gl_LightSource[0].position));"
        "    NdotL = max(dot(normal, lightDir), 0.0);"
        "    diffuse = gl_FrontMaterial.diffuse.xyz * gl_LightSource[0].diffuse.xyz;"
        "    ambient = gl_FrontMaterial.ambient.xyz * gl_LightSource[0].ambient.xyz;"
        "    globalAmbient = gl_LightModel.ambient.xyz * gl_FrontMaterial.ambient.xyz;"

        "    if (NdotL > 0.0) {"
        "       NdotHV = max(dot(normal, normalize(gl_LightSource[0].halfVector.xyz)),0.0);"
        "       specular = gl_FrontMaterial.specular.xyz * gl_LightSource[0].specular.xyz * pow(NdotHV,gl_FrontMaterial.shininess);"
        "    }"
        "    gl_FragColor = vec4(globalAmbient + NdotL * diffuse + ambient + specular,1.0);"
        "}";
static GLfloat ambient[]={0.6f,0.6f,0.6f,1.0f};
static GLfloat specular[]={0.6f,0.6f,0.6f,1.0f};
static GLfloat emissive[]={0.7f,0.7f,0.7f,1.0f};
static GLfloat diffuse[]={0.9f,0.3f,0.3f,1.0f};
static GLint shininess=48;

static GLfloat lightPosition[4] = { 200.0, -200.0, 700.0, 1.0 };
static GLfloat lightModel[] = { 0.3f, 0.3f, 0.3f, 1.0f };
static GLfloat ambientLight[] = { 0.6f, 0.6f, 0.6f, 1.0f };
static GLfloat diffuseLight[] = { 1.0f, 1.0f, 1.0f, 1.0f };
static GLfloat specularLight[] = { 1.0f, 1.0f, 1.0f, 1.0f };

//char *GLWidget::getSource(const char *fileName){
//    QFile f(fileName);
//    if(!f.open(QIODevice::ReadOnly | QIODevice::Text)){
//        qDebug()<<"read file "<<fileName<<" failed"<<endl;
//        return NULL;
//    }
//    QByteArray qba= f.readAll();
//    //qDebug()<<qba.size()<<endl;
//    char *qbac=qba.data();
//    char *ret=new char[qba.size()+1];
//    memcpy(ret,qbac,qba.size());
//    ret[qba.size()]=NULL;
//    //qDebug()<<ret<<endl;
//    return ret;
//}

//void GLWidget::printShaderInfoLog(GLuint obj)
//{
//    int infologLength = 0;
//    int charsWritten  = 0;
//    char *infoLog;

//    glGetShaderiv(obj, GL_INFO_LOG_LENGTH,&infologLength);

//    if (infologLength > 0)
//    {
//        infoLog = (char *)malloc(infologLength);
//        glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog);
//        qDebug()<<infoLog<<endl;
//        free(infoLog);
//    }
//}

//void GLWidget::printProgramInfoLog(GLuint obj)
//{
//    int infologLength = 0;
//    int charsWritten  = 0;
//    char *infoLog;

//    glGetProgramiv(obj, GL_INFO_LOG_LENGTH,&infologLength);

//    if (infologLength > 0)
//    {
//        infoLog = (char *)malloc(infologLength);
//        glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog);
//        qDebug()<<infoLog<<endl;
//        free(infoLog);
//    }
//}

//! [6]
void GLWidget::initializeGL()
{
    program=new QGLShaderProgram;
    if(!program->addShaderFromSourceFile(QGLShader::Fragment,"frag.glsl")){
        qDebug()<<program->log()<<endl;
    }
    if(!program->addShaderFromSourceFile(QGLShader::Vertex,"vert.glsl")){
        qDebug()<<program->log()<<endl;
    }

//    glewInit();

//    v=glCreateShader(GL_VERTEX_SHADER);
//    f=glCreateShader(GL_FRAGMENT_SHADER);

//    char *vs,*fs;
//    vs=this->getSource("vert.glsl");
//    fs=this->getSource("frag.glsl");

//    qDebug()<<vs<<endl;
//    qDebug()<<fs<<endl;
//    const char *ff=fs;
//    const char *vv=vs;
//    glShaderSource(v,1,&vv,NULL);
//    glShaderSource(f,1,&ff,NULL);

//    delete fs;
//    delete vs;
//    fs=NULL;vs=NULL;

//    glCompileShader(v);
//    this->printShaderInfoLog(v);
//    glCompileShader(f);
//    this->printShaderInfoLog(f);

//    p=glCreateProgram();

//    glAttachShader(p,v);
//    glAttachShader(p,f);
//    glLinkProgram(p);
    program->link();
    //radiusLocation=program->uniformLocation("sphere_radius");

//    GLuint loc1 = glGetAttribLocation(p,"sphere_radius");
//    GLuint loc2 = glGetAttribLocation(p,"sphere_color");
    //GLuint loc1 = glGetUniformLocation(p, "sphere_radius");
    locRadius=program->attributeLocation("sphere_radius");
    locColor=program->attributeLocation("sphere_color");

    glClearColor(0.1f, 0.1f, 0.1f, 1.0f );

    logo = new QtLogo(this, 64);
    logo->setColor(qtGreen.dark());

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glShadeModel(GL_SMOOTH);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_MULTISAMPLE);
    glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
    glLightfv(GL_LIGHT0,GL_AMBIENT,ambientLight);
    glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuseLight);
    glLightfv(GL_LIGHT0,GL_SPECULAR,specularLight);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT,lightModel);

    for(int i = 0; i < DATA_SIZE; i++){
        vSmallStars[i][0] = (GLfloat)(rand() % 800)-400;
        vSmallStars[i][1] = (GLfloat)(rand() % 600)-300;
        vSmallStars[i][2] = (GLfloat)(rand() % 600)-300;
    }

    glGenTextures(2, textureObjects);

    glTexEnvi(GL_POINT_SPRITE_NV, GL_COORD_REPLACE_NV, GL_TRUE);
    //glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR);
    glDisable(GL_LINE_SMOOTH);
    glDisable(GL_POINT_SMOOTH);
    glDisable(GL_POLYGON_SMOOTH);

    glEnable(GL_COLOR_MATERIAL);
    glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,ambient);
    //glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,diffuse);
    glMaterialfv(GL_FRONT_AND_BACK,GL_EMISSION,emissive);
    glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,specular);
    glMateriali(GL_FRONT_AND_BACK,GL_SHININESS,shininess);



    //use draw lists to accelerate
    sphereList=glGenLists(1);
    glNewList(sphereList, GL_COMPILE);
    glColor4f(1.0f, 0.5f, 1.0f, 1.0f);
    //glUniform1f(loc1, POINT_SIZE);
    glBegin(GL_QUADS);
    for(int i = 0; i < DATA_SIZE; i++){
        //glVertexAttrib4fv(loc2, diffuse);
        //glVertexAttrib1f(loc1, POINT_SIZE);
        program->setAttributeValue(locRadius,POINT_SIZE);
        program->setAttributeValue(locColor,diffuse[0],diffuse[1],diffuse[2],diffuse[3]);
        glTexCoord2f(0.0f, 0.0f);
        glVertex3f(vSmallStars[i][0],vSmallStars[i][1],vSmallStars[i][2]);

        //glVertexAttrib4fv(loc2, diffuse);
        //glVertexAttrib1f(loc1, POINT_SIZE);
        glTexCoord2f(1.0f, 0.0f);
        glVertex3f(vSmallStars[i][0],vSmallStars[i][1],vSmallStars[i][2]);

        //glVertexAttrib4fv(loc2, diffuse);
        //glVertexAttrib1f(loc1, POINT_SIZE);
        glTexCoord2f(1.0f, 1.0f);
        glVertex3f(vSmallStars[i][0],vSmallStars[i][1],vSmallStars[i][2]);

        //glVertexAttrib4fv(loc2, diffuse);
        //glVertexAttrib1f(loc1, POINT_SIZE);
        glTexCoord2f(0.0f, 1.0f);
        glVertex3f(vSmallStars[i][0],vSmallStars[i][1],vSmallStars[i][2]);
    }
    glEnd();
    glEndList();
}
//! [6]

//! [7]
void GLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    //glTranslatef(0.0, 0.0, -10.0);
    glRotatef(xRot / 16.0, 1.0, 0.0, 0.0);
    glRotatef(yRot / 16.0, 0.0, 1.0, 0.0);
    glRotatef(zRot / 16.0, 0.0, 0.0, 1.0);

    //glRectf(100.0f,50.0f,200.0f,300.0f);

    //glEnable(GL_POINT_SPRITE_NV);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, textureObjects[0]);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    program->bind();
//    glUseProgram(p);
    //program->setUniformValue(radiusLocation,POINT_SIZE);
    // Draw small stars
//    int i;
//    //glPointSize(POINT_SIZE);          // 1.0
//    glColor4f(1.0f, 0.5f, 1.0f, 1.0f);

//    glBegin(GL_QUADS);
//    for(i = 0; i < DATA_SIZE; i++){
//        glTexCoord2f(0.0f, 0.0f);
//        glVertex3f(vSmallStars[i][0]-TEMP_SIZE,vSmallStars[i][1]-TEMP_SIZE,vSmallStars[i][2]);
//        glTexCoord2f(1.0f, 0.0f);
//        glVertex3f(vSmallStars[i][0]+TEMP_SIZE,vSmallStars[i][1]-TEMP_SIZE,vSmallStars[i][2]);
//        glTexCoord2f(1.0f, 1.0f);
//        glVertex3f(vSmallStars[i][0]+TEMP_SIZE,vSmallStars[i][1]+TEMP_SIZE,vSmallStars[i][2]);
//        glTexCoord2f(0.0f, 1.0f);
//        glVertex3f(vSmallStars[i][0]-TEMP_SIZE,vSmallStars[i][1]+TEMP_SIZE,vSmallStars[i][2]);
//    }
//    glEnd();

    //call list
    glCallList(sphereList);

    //qDebug()<<program->log()<<endl;
    //logo->draw();
}
//! [7]

//! [8]
void GLWidget::resizeGL(int width, int height)
{
    //int side = qMin(width, height);
    //glViewport((width - side) / 2, (height - side) / 2, side, side);

    float aspectRatio=width/(float)height;
    glViewport(0, 0, width, height);

    //qDebug()<<aspectRatio<<endl;
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glOrtho(-aspectRatio*farRatio*400, aspectRatio*farRatio*400, -farRatio*400, farRatio*400, -800.0, 800.0);
    //qDebug()<<"resizeGL"<<endl;
    glMatrixMode(GL_MODELVIEW);
}
//! [8]

//! [9]
void GLWidget::mousePressEvent(QMouseEvent *event)
{
    lastPos = event->pos();
}
//! [9]

//! [10]
void GLWidget::mouseMoveEvent(QMouseEvent *event)
{
    int dx = event->x() - lastPos.x();
    int dy = event->y() - lastPos.y();

    if (event->buttons() & Qt::LeftButton) {
        setXRotation(xRot + 2 * dy);
        setYRotation(yRot + 2 * dx);
    } else if (event->buttons() & Qt::RightButton) {
        setXRotation(xRot + 2 * dy);
        setZRotation(zRot + 2 * dx);
    }
    lastPos = event->pos();
}
//! [10]

void GLWidget::wheelEvent(QWheelEvent *event){
    farRatio=farRatio+(float)(event->delta())/1000;
    if(farRatio<0.05){
        farRatio=0.05;
    }
    if(farRatio>20){
        farRatio=20;
    }
    //qDebug()<<farRatio<<endl;
    this->resizeGL(this->width(),this->height());
    this->repaint();
}
